package cloudspace.vm.javassist;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;


/**
 * A factory for creating Command objects. This factory takes a command provided
 * in a javassist configuration file and produces a command.
 */
public class CommandFactory
{
    private static Logger logger = Logger.getLogger( CommandFactory.class );

    /** The macro hash. */
    public static HashMap<String, MacroDefinition> macroHash = new HashMap<String, MacroDefinition>();


    /**
     * Creates a command object from a list of arguments parsed from the command
     * line.
     * 
     * @param args
     *            the arguments parsed out of a javassist configuration file
     * 
     * @return the created command
     * 
     * @throws MalformedCommandException
     *             the malformed command exception
     * @throws MalformedMacroException
     *             the malformed macro exception
     */
    public static CommandInterface createCommand( List<String> args )
        throws MalformedCommandException,
        MalformedMacroException
    {
        cleanArgs( args );
        String commandType = args.get( 0 );
        String command = args.get( args.size() - 1 );
        Pattern macroPattern = Pattern.compile( "%[a-zA-z0-9]+\\([^\\)]*\\)" );
        Matcher macros = macroPattern.matcher( command );
        // Find Macros in the replacement text
        while ( macros.find() )
        {
            String inlineMacro = macros.group();
            macros = macroPattern.matcher( command.substring( macros.end() ) );
            MacroCall inline = new MacroCall( inlineMacro );
            MacroDefinition definition = macroHash.get( inline.getMacroName() );
            if ( definition == null )
            {
                throw new MalformedMacroException( "The macro call, "
                    + inline.getMacroName()
                    + ", does not match any known definitions" );
            }
            String replacement = definition.getTailoredCommand( inline );
            String fixedCommand = command.replace( inlineMacro, replacement );
            args.set( args.size() - 1, fixedCommand );
        }
        try
        {
            Class<?> clazz = Class.forName( "cloudspace.vm.javassist."
                + commandType );
            Constructor<?> constr = clazz.getDeclaredConstructor( new Class<?>[] { java.util.List.class } );
            CommandInterface reflectCommand = (CommandInterface)constr.newInstance( new Object[] { args } );
            return reflectCommand;
        }
        catch ( Exception e )
        {
            logger.error( "The command " + commandType
                + " does not have a matching command class.", e );
        }
        throw new MalformedCommandException( "The translationType " + command
            + " is unknown in command:  " + args.toString() );
    }


    /**
     * this cleans an argument list to remove arguments that are known to add no
     * meaning to a command.
     * 
     * @param args
     *            the args
     */
    private static void cleanArgs( List<String> args )
    {
        args.remove( "with" );

    }


    /**
     * Registers a macro to the command factory. The macros are automatically
     * used when creating commands to create clean commands without macro
     * references in them.
     * 
     * @param curMacro
     *            the cur macro
     */
    public static void registerMacro( MacroDefinition curMacro )
    {
        macroHash.put( curMacro.getMacroName(), curMacro );

    }
}
